package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
	"kuangstudy-mall/srvs/stocks-srv/global"
	"kuangstudy-mall/srvs/stocks-srv/model"
	"kuangstudy-mall/srvs/stocks-srv/proto"
)

type GoodsStocksServiceHandler struct {
}

// 设置库存
func (handler *GoodsStocksServiceHandler) SetGoodsStocks(ctx context.Context, req *proto.GoodsStocksInfo) (*proto.GoodsStocksResponse, error) {
	goodsStocks := model.GoodsStocks{}
	// 根据商品id查询是否存在商品信息
	global.DB.Where(&model.GoodsStocks{GoodsId: req.GoodsId}).First(&goodsStocks)
	goodsStocks.GoodsId = req.GoodsId
	goodsStocks.Stocks = req.Num
	//开始保存库存---如果查询没有就是新增，如果存在就是修改
	global.DB.Save(&goodsStocks)
	return &proto.GoodsStocksResponse{Flag: true}, nil
}

// 根据商品ID查询库存服务
func (handler *GoodsStocksServiceHandler) GetGoodsStocks(ctx context.Context, req *proto.GoodsStocksRequest) (*proto.GoodsStocksInfo, error) {
	goodsStocks := model.GoodsStocks{}
	// 根据商品id查询是否存在商品信息
	if result := global.DB.Where(&model.GoodsStocks{GoodsId: req.GoodsId}).First(&goodsStocks); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "找不到商品的库存信息!")
	}

	return &proto.GoodsStocksInfo{
		GoodsId: goodsStocks.GoodsId,
		Num:     goodsStocks.Stocks,
	}, nil
}

// 库存扣减服务 .其实就是用户在购物车的模块中点击下单的时候会保存订单，然后扣减库存，所以商品是多个不是一个
func (handler *GoodsStocksServiceHandler) GoodsStocksSell(ctx context.Context, req *proto.GoodsSellListRequest) (*proto.GoodsStocksResponse, error) {
	tx := global.DB.Begin()
	sellDetail := model.StockSellDetail{
		OrderNo: req.OrderNo,
		Status:  1,
	}
	var details []model.GoodsDetail
	for _, goodsInfo := range req.GoodsList {
		details = append(details, model.GoodsDetail{
			GoodsId: goodsInfo.GoodsId,
			Num:     goodsInfo.Num,
		})

		cacheKey := fmt.Sprintf("ksd:goods:order:%d", goodsInfo.GoodsId)
		mutex := global.ReisSync.NewMutex(cacheKey)
		if err := mutex.Lock(); err != nil {
			return nil, status.Errorf(codes.Internal, "获取redis的分布式锁失败!")
		}
		// 1： 判断购物车中的每个商品ID是否真实存在，如果不存在就直接提示错误信息
		goodsStocks := model.GoodsStocks{}
		if result := tx.Where(&model.GoodsStocks{GoodsId: goodsInfo.GoodsId}).First(&goodsStocks); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Errorf(codes.NotFound, "找不到商品的库存信息!")
		}
		// 2: 如果商品存在，就开始判断商品的库存是否充足
		// goodsInfo.Num: 用户购物车购买的商品数量
		// goodsStocks.Stocks : 商品实际的库存真实数量
		if goodsInfo.Num > goodsStocks.Stocks {
			tx.Rollback()
			return nil, status.Errorf(codes.ResourceExhausted, "商品库存不足!")
		}
		// 3: 开始扣减库存
		goodsStocks.Stocks = goodsStocks.Stocks - goodsInfo.Num
		// 4：开始更新数据量商品的库存信息
		tx.Save(&goodsStocks)
		// 5：释放锁
		if ok, err := mutex.Unlock(); !ok || err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "释放redis的分布式锁失败!")
		}
	}

	sellDetail.Detail = details
	//写selldetail表
	if result := tx.Create(&sellDetail); result.RowsAffected == 0 {
		tx.Rollback()
		return nil, status.Errorf(codes.Internal, "保存库存扣减历史失败")
	}
	tx.Commit()
	return &proto.GoodsStocksResponse{Flag: true}, nil
}

// 库存的归还服务，其实就是指用户在下单以后，如果没有支付的订单，或者超时的订单，或者退款的订单都会进行库存回归
func (handler *GoodsStocksServiceHandler) GoodsStocksReback(ctx context.Context, req *proto.GoodsSellListRequest) (*proto.GoodsStocksResponse, error) {
	tx := global.DB.Begin()
	for _, goodsInfo := range req.GoodsList {
		cacheKey := fmt.Sprintf("ksd:goods:order:%d", goodsInfo.GoodsId)
		mutex := global.ReisSync.NewMutex(cacheKey)
		if err := mutex.Lock(); err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "获取redis的分布式锁失败!")
		}
		// 1： 判断购物车中的每个商品ID是否真实存在，如果不存在就直接提示错误信息
		goodsStocks := model.GoodsStocks{}
		if result := tx.Where(&model.GoodsStocks{GoodsId: goodsInfo.GoodsId}).First(&goodsStocks); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Errorf(codes.NotFound, "找不到商品的库存信息!")
		}

		// 3: 开始扣减库存
		goodsStocks.Stocks = goodsStocks.Stocks + goodsInfo.Num
		// 4：开始更新数据量商品的库存信息
		tx.Save(&goodsStocks)
		// 5：释放锁
		if ok, err := mutex.Unlock(); !ok || err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "释放redis的分布式锁失败!")
		}
	}

	tx.Commit()
	return &proto.GoodsStocksResponse{Flag: true}, nil
}

/*
*
库存归还
*/
func GoodsReback(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
	type OrderInfo struct {
		OrderNo string
	}
	for i := range msgs {
		//既然是归还库存，那么我应该具体的知道每件商品应该归还多少， 但是有一个问题是什么？重复归还的问题
		//所以说这个接口应该确保幂等性， 你不能因为消息的重复发送导致一个订单的库存归还多次， 没有扣减的库存你别归还
		//如果确保这些都没有问题， 新建一张表， 这张表记录了详细的订单扣减细节，以及归还细节
		var orderInfo OrderInfo
		err := json.Unmarshal(msgs[i].Body, &orderInfo)
		if err != nil {
			zap.S().Errorf("解析json失败： %v\n", msgs[i].Body)
			return consumer.ConsumeSuccess, nil
		}

		// 判断该笔订单是否存在归还的库存信息
		tx := global.DB.Begin()
		var sellDetail model.StockSellDetail
		if result := tx.Model(&model.StockSellDetail{}).Where(&model.StockSellDetail{OrderNo: orderInfo.OrderNo, Status: 1}).First(&sellDetail); result.RowsAffected == 0 {
			return consumer.ConsumeSuccess, nil
		}

		//如果查询到那么逐个归还库存
		for _, orderGood := range sellDetail.Detail {
			//update怎么用
			//先查询一下inventory表在， update语句的 update xx set stocks=stocks+num where goods-uid = 1
			if result := tx.Model(&model.GoodsStocks{}).Where(&model.GoodsStocks{GoodsId: orderGood.GoodsId}).Update("stocks", gorm.Expr("stocks+?", orderGood.Num)); result.RowsAffected == 0 {
				tx.Rollback()
				return consumer.ConsumeRetryLater, nil
			}
		}

		// 修改归还明细状态为，意味着这笔订单归还了。后续就不要在继续操作。
		if result := tx.Model(&model.StockSellDetail{}).Where(&model.StockSellDetail{OrderNo: orderInfo.OrderNo}).Update("status", 2); result.RowsAffected == 0 {
			tx.Rollback()
			return consumer.ConsumeRetryLater, nil
		}

		tx.Commit()
		return consumer.ConsumeSuccess, nil
	}
	return consumer.ConsumeSuccess, nil
}
